﻿using System;
using System.Collections.Concurrent;
using System.Net;

namespace ABCat.Shared
{
	public static class WebClientPool
	{
		private static readonly ConcurrentBag<WebClient> WebClients = new ConcurrentBag<WebClient>();

		public static WebClient GetWebClient()
		{
			WebClient item;
			if (WebClients.TryTake(out item)) return item;
			return new WebClient();
		}

		public static WebClientPoolItem GetPoolItem()
		{
			return new WebClientPoolItem();
		}

		public static void PutObject(WebClient item)
		{
			WebClients.Add(item);
		}
	}

	public sealed class WebClientPoolItem : IDisposable
	{
		private WebClient _webClient;

		public WebClient Target { get { return _webClient; } }

		public WebClientPoolItem()
		{
			_webClient = new WebClient(); // WebClientPool.GetWebClient();
		}

		public void Dispose()
		{
			WebClientPool.PutObject(Target);
			_webClient = null;
		}
	}

	public class ObjectPool<T>
	{
		private readonly ConcurrentBag<T> _objects;
		private readonly Func<T> _objectGenerator;

		public ObjectPool(Func<T> objectGenerator)
		{
			if (objectGenerator == null) throw new ArgumentNullException("objectGenerator");
			_objects = new ConcurrentBag<T>();
			_objectGenerator = objectGenerator;
		}

		public ObjectPoolItem<T> GetPoolItem(bool createNew)
		{
			return new ObjectPoolItem<T>(this, createNew);
		}

		public T GetObject(bool createNew = false)
		{
			T item;
			if (!createNew && _objects.TryTake(out item)) return item;
			return _objectGenerator();
		}

		public void PutObject(T item)
		{
			_objects.Add(item);
		}
	}

	public sealed class ObjectPoolItem<T> : IDisposable
	{
		private readonly ObjectPool<T> _owner;
		public T Target { get; private set; }

		public ObjectPoolItem(ObjectPool<T> owner, bool createNew = false)
		{
			_owner = owner;
			Target = owner.GetObject(createNew);
		}

		public void Dispose()
		{
			_owner.PutObject(Target);
		}

		public void Refresh()
		{
			Target = _owner.GetObject(true);
		}
	}
}
